package me.zhengjie.tool;

import java.sql.Timestamp;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.Calendar;
import java.util.Objects;

import static java.time.temporal.TemporalAdjusters.*;

/**
 * <p>
 * <h2>声明：</h2>
 * 本工具类基于5.0 (jdk 1.8) 提供的 java.time.* 包进行时间的转换，格式化以及计算。
 * 使用本工具类需要的JDK版本最低为 1.8。以下有的方法由于工作量问题未声明@since 5.0 (jdk 1.8) ,
 * 此处特别声明，望谅解！
 * </p>
 * <hr />
 * <p>
 * 该工具类中总共涉及7个日期类,在在工具类中的名称以及对应类关系：
 * </p>
 * <ul>
 * <li>date -> java.util.Date</li>
 * <li>sqlDate\SqlDate -> java.sql.Date</li>
 * <li>sqlTime\SqlTime -> java.sql.Time</li>
 * <li>timestamp -> java.sql.Timestamp</li>
 * <li>localDateTime -> java.time.LocalDateTime</li>
 * <li>localDate -> java.time.LocalDate</li>
 * <li>localTime -> java.time.LocalTime</li>
 * </ul>
 * <p>DateUtil主要的组成包括八部分:</p>
 * <ul>
 * <li>时间获取，获取当前时间以及一些特殊时间等。直接通过DateUtil.*获取。eg:DateUtils.getCurrentDate()</li>
 * <li>时间类转换，提供7大类日期时间类的相互转换。通过DateUtil.Convert.*获取</li>
 * <li>时间格式化，提供7大类日期时间到字符串的相互转换。通过DateUtil.Format.*获取</li>
 * <li>时间比较，比较两个时间的大小，以及两个日期之间相差得具体时间（秒/分/时/天/月/年）等。通过DateUtil.Judge.*获取</li>
 * <li>时间计算，对某个时间进行加法计算（减日期就是加负值）。通过DateUtil.Calculate.*获取</li>
 * <li>时区相关工具，获取某个时区当前时间（日期），以及计算某个时区的某个时间在目标时区对应的时间等。通过DateUtil.ZoneConvert.*</li>
 * <li>内置的时间转换器，当使用时间格式化工具时，有的需要提供转换器，工具类提供默认的时间转换器以及可自定义的转换器（DateUtils.TimeFormat.getCustomPattern(String pattern)。），
 * 通过DateUtil.TimeFormat.*访问。
 * </li>
 * <li>内置JDK1.8时区，通过DateUtil.Zone.*.zoneId 访问获得对应的ZoneId实例。</li>
 *
 * </ul>
 *
 * @author MinusZero [hesh@yinhai.com]
 * @since 5.0 (jdk 8)
 */
public class DateUtils {

    /**
     * 私有构造方法。工具类，不允许实例化。
     */
    private DateUtils() {
    }

    /**
     * 获取当前时间
     *
     * @return java.util.Date
     */
    public static java.util.Date getCurrentDate() {
        return Convert.localDateTimeToDate(LocalDateTime.now());
    }

    /**
     * 获取当前时间（根据提供的转换换器）
     *
     * @param format 时间转换器
     * @return 时间字符串
     */
    public static String getCurrentDate(TimeFormat format) {
        if (Objects.isNull(format)) {
            return Const.EMPTY_STRING_OBJECT;
        }
        return format.formatter.format(LocalDateTime.now());
    }

	/**
	 * 获取t 之前day天的时间戳
 	 * @param t 时间t
	 * @param day 之前天数
	 * @return 时间戳
	 */
	public static Timestamp getDateBefore(Timestamp t, int day){
		Calendar now = Calendar.getInstance();
		now.setTime(t);
		now.set(Calendar.DATE,now.get(Calendar.DATE) - day);
		return new Timestamp(now.getTime().getTime());
	}

    /**
     * 传入年月日时分秒得到java.util.Date
     *
     * @param year   年
     * @param month  月
     * @param day    天
     * @param hour   小时
     * @param minute 分钟
     * @param second 秒
     * @return java.util.Date 时间
     */
    public static java.util.Date getDate(int year, int month, int day, int hour, int minute, int second) {
        LocalDateTime localDateTime;
        try {
            localDateTime = LocalDateTime.of(year, month, day, hour, minute, second);
        } catch (Exception e) {
            localDateTime = null;
        }
        return Objects.isNull(localDateTime) ? Const.NULL_UTIL_DATE_OBJECT : Convert.localDateTimeToDate(localDateTime);
    }

    /**
     * 根据提供的时间，获得该月的总共天数
     *
     * @param date 传入时间
     * @return int
     */
    public static int getDaysOfMonth(java.util.Date date) {
        if (Objects.isNull(date)) {
            throw new IllegalArgumentException("date cannot be null");
        }
        LocalDateTime localDateTime = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
        return getDaysOfMonth(localDateTime.getMonth().getValue(), localDateTime.getYear());
    }

    /**
     * 根据提供的年月，获取该年该月的总共天数
     *
     * @param month
     * @param year
     * @return int
     */
    public static int getDaysOfMonth(int month, int year) {
        if ((month == 1) || (month == 3) || (month == 5) || (month == 7) || (month == 8) || (month == 10) || (month == 12)) {
            return 31;
        } else if ((month == 4) || (month == 6) || (month == 9) || (month == 11)) {
            return 30;
        } else {
            if (((year % 4) == 0) && ((year % 100) != 0) || ((year % 400) == 0)) {
                return 29;
            } else {
                return 28;
            }
        }
    }

    /**
     * 根据一个日期，返回该月的第一天
     *
     * @param date 传入时间
     * @return java.util.Date
     */
    public static java.util.Date getFirstDayOfMonth(java.util.Date date) {
        return Convert.localDateTimeToDate
                (getFirstDayOfMonth
                        (Convert.dateToLocalDateTime(date)));
    }

    /**
     * 根据提供的日期，返回该月的最后一天
     *
     * @param date 传入时间
     * @return java.util.Date
     */
    public static java.util.Date getLastDayOfMonth(java.util.Date date) {
        return Convert.localDateTimeToDate
                (getLastDayOfMonth
                        (Convert.dateToLocalDateTime(date)));
    }

    /**
     * 根据提供的日期，返回该年的第一天
     *
     * @param date 传入时间
     * @return java.util.Date
     */
    public static java.util.Date getFirstDayOfYear(java.util.Date date) {
        return Convert.localDateTimeToDate
                (getFirstDayOfYear
                        (Convert.dateToLocalDateTime(date)));
    }

    /**
     * 根据提供的日期，返回概念的最后一天
     *
     * @param date 传入时间
     * @return java.util.Date
     */
    public static java.util.Date getLastDayOfYear(java.util.Date date) {
        return Convert.localDateTimeToDate
                (getLastDayOfYear
                        (Convert.dateToLocalDateTime(date)));
    }

    /**
     * 根据提供的日期以及星期几（DayOfWeek），返回该月的第一个出现该星期的日期。
     * eg:
     * <p>如果传入的时间为:"2018-09-12 13:55:00",星期为：DayOfWeek.THURSDAY，
     * 则返回2018年9月第一次出现星期四的时间: "2018-09-06 13:55:00"
     * </p>
     *
     * @param date      传入时间
     * @param dayOfWeek 星期几
     * @return java.util.Date
     */
    public static java.util.Date getFirstInMonth(java.util.Date date, DayOfWeek dayOfWeek) {
        return Convert.localDateTimeToDate
                (getFirstInMonth
                        (Convert.dateToLocalDateTime(date), dayOfWeek));
    }

    /**
     * 根据提供的日期和星期（DayOfWeek）,返回该日期后下一个DayOfWeek的日期。
     * eg:
     * <p>如果传入的时间为:"2018-09-12 13:55:00"（2018-09-12为星期三）,星期为：DayOfWeek.THURSDAY，
     * 则返回2018年9月第一次出现星期四的时间: "2018-09-13 13:55:00"
     * </p>
     *
     * @param date      传入时间
     * @param dayOfWeek 星期几
     * @return java.util.Date
     */
    public static java.util.Date getNext(java.util.Date date, DayOfWeek dayOfWeek) {
        return Convert.localDateTimeToDate
                (getNext
                        (Convert.dateToLocalDateTime(date), dayOfWeek));
    }

    /**
     * 根据提供的日期时间和星期（DayOfWeek），返回该日期时间之前上一个DayOfWeek的日期。
     * eg:
     * <p>如果传入的时间为:"2018-09-12 13:55:00",星期为：DayOfWeek.THURSDAY，
     * 则返回2018年9月第一次出现星期四的时间: "2018-09-06 13:55:00"
     * </p>
     *
     * @param date      传入时间
     * @param dayOfWeek 星期几
     * @return java.util.Date
     */
    public static java.util.Date getPrevious(java.util.Date date, DayOfWeek dayOfWeek) {
        return Convert.localDateTimeToDate
                (getPrevious
                        (Convert.dateToLocalDateTime(date), dayOfWeek));
    }

    /**
     * 根据提供的日期返回当月的第一天
     *
     * @param date 传入时间
     * @return java.time.LocalDateTime
     */
    public static LocalDateTime getFirstDayOfMonth(LocalDateTime date) {
        if (Objects.isNull(date)) {
            return Const.NULL_LOCALDATETIME_OBJECT;
        }
        return date.with(firstDayOfMonth());
    }

    /**
     * 根据提供的日期返回当月的最后一天
     *
     * @param date 传入时间
     * @return java.time.LocalDateTime
     */
    public static LocalDateTime getLastDayOfMonth(LocalDateTime date) {
        if (Objects.isNull(date)) {
            return Const.NULL_LOCALDATETIME_OBJECT;
        }
        return date.with(lastDayOfMonth());
    }

    /**
     * 根据提供的日期返回当年的第一天
     *
     * @param date 传入时间
     * @return java.time.LocalDateTime
     */
    public static LocalDateTime getFirstDayOfYear(LocalDateTime date) {
        if (Objects.isNull(date)) {
            return Const.NULL_LOCALDATETIME_OBJECT;
        }
        return date.with(firstDayOfYear());
    }

    /**
     * 根据提供的日期返回当年的最后一天
     *
     * @param date 传入时间
     * @return java.time.LocalDateTime
     */
    public static LocalDateTime getLastDayOfYear(LocalDateTime date) {
        if (Objects.isNull(date)) {
            return Const.NULL_LOCALDATETIME_OBJECT;
        }
        return date.with(lastDayOfYear());
    }

    /**
     * 根据提供的日期以及星期几（DayOfWeek），返回该月的第一个出现该星期的日期。
     * eg:
     * <p>如果传入的时间为:"2018-09-12 13:55:00",星期为：DayOfWeek.THURSDAY，
     * 则返回2018年9月第一次出现星期四的时间: "2018-09-06 13:55:00"
     * </p>
     *
     * @param date      传入时间
     * @param dayOfWeek 星期几
     * @return java.time.LocalDateTime
     */
    public static LocalDateTime getFirstInMonth(LocalDateTime date, DayOfWeek dayOfWeek) {
        if (Objects.isNull(date) || Objects.isNull(dayOfWeek)) {
            return Const.NULL_LOCALDATETIME_OBJECT;
        }
        return date.with(firstInMonth(dayOfWeek));
    }

    /**
     * 根据提供的日期和星期（DayOfWeek）,返回该日期后下一个DayOfWeek的日期。
     * eg:
     * <p>如果传入的时间为:"2018-09-12 13:55:00"（2018-09-12为星期三）,星期为：DayOfWeek.THURSDAY，
     * 则返回2018年9月第一次出现星期四的时间: "2018-09-13 13:55:00"
     * </p>
     *
     * @param date      传入时间
     * @param dayOfWeek 星期几
     * @return java.time.LocalDateTime
     */
    public static LocalDateTime getNext(LocalDateTime date, DayOfWeek dayOfWeek) {
        if (Objects.isNull(date) || Objects.isNull(dayOfWeek)) {
            return Const.NULL_LOCALDATETIME_OBJECT;
        }
        return date.with(next(dayOfWeek));
    }

    /**
     * 根据提供的日期时间和星期（DayOfWeek），返回该日期时间之前上一个DayOfWeek的日期。
     * eg:
     * <p>如果传入的时间为:"2018-09-12 13:55:00",星期为：DayOfWeek.THURSDAY，
     * 则返回2018年9月第一次出现星期四的时间: "2018-09-06 13:55:00"
     * </p>
     *
     * @param date      传入时间
     * @param dayOfWeek 星期几
     * @return java.time.LocalDateTime
     */
    public static LocalDateTime getPrevious(LocalDateTime date, DayOfWeek dayOfWeek) {
        if (Objects.isNull(date) || Objects.isNull(dayOfWeek)) {
            return Const.NULL_LOCALDATETIME_OBJECT;
        }
        return date.with(previous(dayOfWeek));
    }

    /**
     * 提供默认JDk1.8中全部支持的时区ZoneId,访问方式：
     * <p>
     * eg: Zone.CTT.zoneId
     * </p>
     * <p>
     * 通过以上方式可获得对应的ZoneId： CTT -> "Asia/Shanghai"。
     * 当本工具中的时区不能满足需求时，创建ZoneId的方式有两种：
     * <ul>
     * <li>
     * 第一种： ZoneId.of("Asia/Shanghai");
     * </li>
     * <li>
     * 第二种： TimeZone.getTimeZone("CTT").toZoneId();
     * </li>
     * </ul>
     * </p>
     */
    public enum Zone {
        CTT("Asia/Shanghai"),
        ART("Africa/Cairo"),
        CNT("America/St_Johns"),
        PRT("America/Puerto_Rico"),
        PNT("America/Phoenix"),
        PLT("Asia/Karachi"),
        AST("America/Anchorage"),
        BST("Asia/Dhaka"),
        CST("America/Chicago"),
        EST("-05:00"),
        HST("-10:00"),
        JST("Asia/Tokyo"),
        IST("Asia/Kolkata"),
        AGT("America/Argentina/Buenos_Aires"),
        NST("Pacific/Auckland"),
        MST("-07:00"),
        AET("Australia/Sydney"),
        BET("America/Sao_Paulo"),
        PST("America/Los_Angeles"),
        ACT("Australia/Darwin"),
        SST("Pacific/Guadalcanal"),
        VST("Asia/Ho_Chi_Minh"),
        CAT("Africa/Harare"),
        ECT("Europe/Paris"),
        EAT("Africa/Addis_Ababa"),
        IET("America/Indiana/Indianapolis"),
        MIT("Pacific/Apia"),
        NET("Asia/Yerevan");

        private transient ZoneId zoneId;

        Zone(String zoneId) {
            this.zoneId = ZoneId.of(zoneId);
        }

        public ZoneId getZoneId() {
            return this.zoneId;
        }
    }

    /**
     * <p>日期时间工具类的内部常量类，其中提供了公用的空指针时返回的对象以及默认的时间格式转换器。</p>
     *
     * @since 5.0 (jdk 1.8)
     */
    private static class Const {

        private static final java.util.Date NULL_UTIL_DATE_OBJECT = null;
        private static final Timestamp NULL_SQL_TIMESTAMP_OBJECT = null;
        private static final java.sql.Date NULL_SQL_DATE_OBJECT = null;
        private static final java.sql.Time NULL_SQL_TIME_OBJECT = null;
        private static final LocalDateTime NULL_LOCALDATETIME_OBJECT = null;
        private static final LocalDate NULL_LOCALDATE_OBJECT = null;
        private static final LocalTime NULL_LOCALTIME_OBJECT = null;
        private static final String EMPTY_STRING_OBJECT = null;
        /**
         * 获取默认时间格式: yyyy-MM-dd HH:mm:ss
         */
        private static final TimeFormat DEFAULT_DATETIME_FORMATTER = TimeFormat.YYYY_MM_DD_HH_MM_SS_LINE;
        /**
         * 获取默认日期格式: yyyy-MM-dd
         */
        private static final TimeFormat DEFAULT_DATE_FORMATTER = TimeFormat.YYYY_MM_DD_LINE;
        /**
         * 获取默认时间格式: HH:mm:ss
         */
        private static final TimeFormat DEFAULT_TIME_FORMATTER = TimeFormat.HH_MM_SS_COLON;
        /**
         * 私有构造，内部静态常量置放类，不可实例
         */
        private Const() {
        }
    }

    /**
     * Convert主要用于日期类与日期类之间的转换工具.
     * 其中的日期类主要包括：
     * <ul>
     * <li>java.util.Date</li>
     * <li>java.sql.Time</li>
     * <li>java.sql.Date</li>
     * <li>java.sql.Timestamp</li>
     * <li>java.time.LocalDate</li>
     * <li>java.time.LocalTime</>
     * <li>java.time.localDateTime</li>
     * </ul>
     *
     * @since 5.0 (jdk 1.8)
     */
    public static class Convert {

        /**
         * 私有构造，工具类不允许实例化
         */
        private Convert() {
        }
        //java.time.* -> java.util.Date

        /**
         * java.time.LocalDateTime 转 java.util.date
         *
         * @param localDateTime 被转换的时间
         * @return java.util.Date
         * @since 5.0 (jdk 1.8)
         */
        public static java.util.Date localDateTimeToDate(LocalDateTime localDateTime) {
            if (Objects.isNull(localDateTime)) {
                return Const.NULL_UTIL_DATE_OBJECT;
            }
            return java.util.Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
        }

        /**
         * java.time.LocalDate 转 java.util.date
         *
         * @param localDate 被转换的时间
         * @return java.util.Date
         * @since 5.0 (jdk 1.8)
         */
        public static java.util.Date localDateToDate(LocalDate localDate) {
            if (Objects.isNull(localDate)) {
                return Const.NULL_UTIL_DATE_OBJECT;
            }
            return java.util.Date.from(localDate.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant());
        }

        /**
         * java.time.LocalTime 转 java.util.date
         *
         * @param localTime 被转换的时间
         * @return java.util.Date
         * @since 5.0 (jdk 1.8)
         */
        public static java.util.Date localTimeToDate(LocalTime localTime) {
            if (Objects.isNull(localTime)) {
                return Const.NULL_UTIL_DATE_OBJECT;
            }
            return localDateTimeToDate(localTime.atDate(LocalDate.now()));
        }

        //java.util.Date -> java.time.*

        /**
         * java.util.Date 转 java.time.LocalTime
         *
         * @param date 被转换的时间
         * @return java.time.LocalTime
         * @since 5.0 (jdk 1.8)
         */
        public static LocalTime dateToLocalTime(java.util.Date date) {
            return Objects.isNull(date) ? Const.NULL_LOCALTIME_OBJECT
                    : date.toInstant().atZone(ZoneId.systemDefault()).toLocalTime();
        }

        /**
         * java.util.Date 转 java.time.LocalDate
         *
         * @param date 被转换的时间
         * @return java.time.LocalDate
         * @since 5.0 (jdk 1.8)
         */
        public static LocalDate dateToLocalDate(java.util.Date date) {
            return Objects.isNull(date) ? Const.NULL_LOCALDATE_OBJECT
                    : date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
        }

        /**
         * java.util.Date To java.time.LocalDateTime
         *
         * @param date 被转换的时间
         * @return java.time.LocalDateTime
         * @since 5.0 (jdk 1.8)
         */
        public static LocalDateTime dateToLocalDateTime(java.util.Date date) {
            return Objects.isNull(date) ? Const.NULL_LOCALDATETIME_OBJECT
                    : date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
        }


        //java.time.* -> java.sql.Timestamp

        /**
         * java.time.LocalDateTime To java.time.Timestamp
         *
         * @param localDateTime 被转换的时间
         * @return java.sql.Timestamp
         */
        public static Timestamp localDateTimeToTimestamp(LocalDateTime localDateTime) {
            if (Objects.isNull(localDateTime)) {
                return Const.NULL_SQL_TIMESTAMP_OBJECT;
            }
            return Timestamp.valueOf(localDateTime);
        }

        /**
         * java.time.LocalDate To java.time.Timestamp
         *
         * @param localDate 被转换的时间
         * @return java.sql.Timestamp
         * @since 5.0 (jdk 1.8)
         */
        public static Timestamp localDateToTimestamp(LocalDate localDate) {
            if (Objects.isNull(localDate)) {
                return Const.NULL_SQL_TIMESTAMP_OBJECT;
            }
            return Timestamp.valueOf(localDate.atStartOfDay());
        }

        /**
         * java.time.LocalTime To java.time.Timestamp
         *
         * @param localTime 被转换的时间
         * @return java.sql.Timestamp
         * @since 5.0 (jdk 1.8)
         */
        public static Timestamp localTimeToTimestamp(LocalTime localTime) {
            if (Objects.isNull(localTime)) {
                return Const.NULL_SQL_TIMESTAMP_OBJECT;
            }
            return Timestamp.valueOf(localTime.atDate(LocalDate.now()));
        }

        //java.sql.Timestamp -> java.time.*

        /**
         * java.time.Timestamp To java.time.LocalTime
         *
         * @param timestamp 被转换的时间
         * @return java.time.LocalTime
         * @since 5.0 (jdk 1.8)
         */
        public static LocalTime timestampToLocalTime(Timestamp timestamp) {
            return Objects.isNull(timestamp) ? Const.NULL_LOCALTIME_OBJECT
                    : timestamp.toLocalDateTime().toLocalTime();
        }

        /**
         * java.time.Timestamp To java.time.LocalDate
         *
         * @param timestamp 被转换的时间
         * @return java.time.LocalDate
         * @since 5.0 (jdk 1.8)
         */
        public static LocalDate timestampToLocalDate(Timestamp timestamp) {
            return Objects.isNull(timestamp) ? Const.NULL_LOCALDATE_OBJECT
                    : timestamp.toLocalDateTime().toLocalDate();
        }

        /**
         * java.time.Timestamp To java.time.LocalDateTime
         *
         * @param timestamp 被转换的时间
         * @return java.time.LocalDateTime
         * @since 5.0 (jdk 1.8)
         */
        public static LocalDateTime timestampToLocalDateTime(Timestamp timestamp) {
            return Objects.isNull(timestamp) ? Const.NULL_LOCALDATETIME_OBJECT
                    : timestamp.toLocalDateTime();
        }

        //java.time.* -> java.sql.Date

        /**
         * java.time.LocalDateTime To java.sql.Date
         *
         * @param localDateTime 被转换的时间
         * @return java.sql.Date
         * @since 5.0 (jdk 1.8)
         */
        public static java.sql.Date localDateTimeToSqlDate(LocalDateTime localDateTime) {
            if (Objects.isNull(localDateTime)) {
                return Const.NULL_SQL_DATE_OBJECT;
            }
            return java.sql.Date.valueOf(localDateTime.toLocalDate());
        }

        /**
         * java.time.LocalDate To java.sql.Date
         *
         * @param localDate 被转换的时间
         * @return java.sql.Date
         * @since 5.0 (jdk 1.8)
         */
        public static java.sql.Date localDateToSqlDate(LocalDate localDate) {
            if (Objects.isNull(localDate)) {
                return Const.NULL_SQL_DATE_OBJECT;
            }
            return java.sql.Date.valueOf(localDate);
        }

        //java.sql.Date -> java.time.*

        /**
         * java.sql.Date To java.time.LocalDate
         *
         * @param date 被转换的时间
         * @return java.time.LocalDate
         * @since 5.0 (jdk 1.8)
         */
        public static LocalDate sqlDateToLocalDate(java.sql.Date date) {
            return Objects.isNull(date) ? Const.NULL_LOCALDATE_OBJECT : date.toLocalDate();
        }

        /**
         * java.sql.Date To java.time.LocalDateTime.
         * eg: java.sql.Date:"2018-08-12" -> java.time.LocalDateTime:"2018-08-12 00:00:00"
         *
         * @param date 被转换的时间
         * @return java.time.LocalDateTime
         * @since 5.0 (jdk 1.8)
         */
        public static LocalDateTime sqlDateToLocalDateTime(java.sql.Date date) {
            return Objects.isNull(date) ? Const.NULL_LOCALDATETIME_OBJECT : date.toLocalDate().atStartOfDay();
        }

        //java.time.* -> java.sql.Time

        /**
         * java.time.LocalDateTime To java.sql.Time
         *
         * @param localDateTime 被转换的时间
         * @return java.sql.Time
         * @since 5.0 (jdk 1.8)
         */
        public static java.sql.Time localDateTimeToSqlTime(LocalDateTime localDateTime) {
            if (Objects.isNull(localDateTime)) {
                return Const.NULL_SQL_TIME_OBJECT;
            }
            return java.sql.Time.valueOf(localDateTime.toLocalTime());
        }

        /**
         * java.time.LocalTime To java.sql.Time
         *
         * @param localTime 被转换的时间
         * @return java.sql.Time
         * @since 5.0 (jdk 1.8)
         */
        public static java.sql.Time localTimeToSqlTime(LocalTime localTime) {
            if (Objects.isNull(localTime)) {
                return Const.NULL_SQL_TIME_OBJECT;
            }
            return java.sql.Time.valueOf(localTime);
        }

        //java.sql.Time -> java.time.*

        /**
         * java.sql.Time To java.time.LocalTime
         *
         * @param time 被转换的时间
         * @return java.time.LocalTime
         * @since 5.0 (jdk 1.8)
         */
        public static LocalTime sqlTimeToLocalTime(java.sql.Time time) {
            return Objects.isNull(time) ? Const.NULL_LOCALTIME_OBJECT : time.toLocalTime();
        }

        /**
         * java.sql.Time To java.time.LocalDateTime
         *
         * @param time 被转换的时间
         * @return java.time.LocalDateTime
         * @since 5.0 (jdk 1.8)
         */
        public static LocalDateTime sqlTimeToLocalDateTime(java.sql.Time time) {
            return Objects.isNull(time) ? Const.NULL_LOCALDATETIME_OBJECT : time.toLocalTime().atDate(LocalDate.now());
        }

        //java.util.Date -> java.sql.*

        /**
         * java.util.Date To java.sql.Time
         *
         * @param date 被转换的时间
         * @return java.sql.Time
         */
        public static java.sql.Time dateToSqlTime(java.util.Date date) {
            if (Objects.isNull(date)) {
                return Const.NULL_SQL_TIME_OBJECT;
            }
            return new java.sql.Time(date.getTime());
        }

        /**
         * java.util.Date To java.sql.Timestamp
         *
         * @param date 被转换的时间
         * @return java.sql.Timestamp
         */
        public static Timestamp dateToTimestamp(java.util.Date date) {
            if (Objects.isNull(date)) {
                return Const.NULL_SQL_TIMESTAMP_OBJECT;
            }
            return new Timestamp(date.getTime());
        }

        /**
         * java.util.Date To java.sql.Date
         *
         * @param date 被转换的时间
         * @return java.sql.Date
         */
        public static java.sql.Date dateToSqlDate(java.util.Date date) {
            if (Objects.isNull(date)) {
                return Const.NULL_SQL_DATE_OBJECT;
            }
            return new java.sql.Date(date.getTime());
        }
    }

    /**
     * <h2>时间格式化</h2>
     * <p>时间格式化，从整体来看，提供两种方式：
     * <ul>
     * <li>a.时间字符串（java.lang.String）转换为 时间（日期）类；</li>
     * <li>b.时间（日期）类转换为 java.lang.String。</li>
     * </ul>
     * </p>
     * <p>在某些工具方法中，只需要提供时间或者字符串则可以进行时间格式化操作，其默认的格式化转换器的匹配模式：
     * <p>"yyyy-MM-dd HH:mm:ss":</p>
     * <ul>
     * <li>
     * java.util.Date
     * </li>
     * <li>
     * java.sql.Timestamp
     * </li>
     * <li>
     * java.time.LocalDateTime
     * </li>
     * </ul>
     * </p>
     * <p>"yyyy-MM-dd":
     * <ul>
     * <li>
     * java.sql.Date
     * </li>
     * <li>
     * java.time.LocalDate
     * </li>
     * </ul>
     * </p>
     * <p>"HH:mm:ss":
     * <ul>
     * <li>
     * java.sql.Time
     * </li>
     * <li>
     * java.time.LocalTime
     * </li>
     * </ul>
     * </p>
     */
    public static class Format {

        /**
         * 私有构造，工具类不允许实例化
         */
        private Format() {
        }

        /*
         *
         * java.lang.String -> java.time.* / java.sql.* / java.util.Date
         *
         * */
        //String -> java.time.*

        /**
         * 将时间字符串通过格式转换器，转换为时间
         *
         * @param dateStr    时间字符串
         * @param timeFormat 时间格式转化器
         * @return java.time.LocalDateTime
         */
        public static LocalDateTime stringToLocalDateTime(String dateStr, TimeFormat timeFormat) {
            if (InnerTools.isEmpty(dateStr) || Objects.isNull(timeFormat)) {
                return Const.NULL_LOCALDATETIME_OBJECT;
            }
            return LocalDateTime.parse(dateStr, timeFormat.formatter);
        }

        /**
         * 将时间字符串通过（默认）格式转换器，转换为时间
         *
         * @param dateStr 时间字符串
         * @return java.time.LocalDateTime
         */
        public static LocalDateTime stringToLocalDateTime(String dateStr) {
            return stringToLocalDateTime(dateStr, Const.DEFAULT_DATETIME_FORMATTER);
        }

        /**
         * 将时间字符串通过格式转换器，转换为日期
         *
         * @param dateStr    时间字符串
         * @param timeFormat 时间格式转化器
         * @return java.time.LocalDate
         */
        public static LocalDate stringToLocalDate(String dateStr, TimeFormat timeFormat) {
            if (InnerTools.isEmpty(dateStr) || Objects.isNull(timeFormat)) {
                return Const.NULL_LOCALDATE_OBJECT;
            }
            return LocalDate.parse(dateStr, timeFormat.formatter);
        }

        /**
         * 将时间字符串通过默认格式转换器，转换为日期
         *
         * @param dateStr 时间字符串
         * @return java.time.LocalDate
         */
        public static LocalDate stringToLocalDate(String dateStr) {
            return stringToLocalDate(dateStr, Const.DEFAULT_DATE_FORMATTER);
        }

        /**
         * 将时间字符串通过格式转换器，转换为时刻
         *
         * @param dateStr    时间字符串
         * @param timeFormat 时间格式转化器
         * @return java.time.LocalTime
         */
        public static LocalTime stringToLocalTime(String dateStr, TimeFormat timeFormat) {
            if (InnerTools.isEmpty(dateStr) || Objects.isNull(timeFormat)) {
                return Const.NULL_LOCALTIME_OBJECT;
            }
            return LocalTime.parse(dateStr, timeFormat.formatter);
        }

        /**
         * 将时间字符串通过默认格式转换器，转换为时间
         *
         * @param dateStr 时间字符串
         * @return java.time.LocalTime
         */
        public static LocalTime stringToLocalTime(String dateStr) {
            return stringToLocalTime(dateStr, Const.DEFAULT_TIME_FORMATTER);
        }


        //String -> java.util.Date

        /**
         * 将时间字符串通过格式转换器，转换为时间
         *
         * @param dateStr    时间字符串
         * @param timeFormat 时间格式转化器
         * @return java.util.Date
         */
        public static java.util.Date stringToDate(String dateStr, TimeFormat timeFormat) {
            return Convert.localDateTimeToDate(stringToLocalDateTime(dateStr, timeFormat));
        }

        /**
         * 将时间字符串通过默认格式转换器，转换为时间
         *
         * @param dateStr 时间字符串
         * @return java.util.Date
         */
        public static java.util.Date stringToDate(String dateStr) {
            return Convert.localDateTimeToDate(stringToLocalDateTime(dateStr));
        }

        //String -> java.sql.*

        /**
         * 将时间字符串通过格式转换器，转换为时间
         *
         * @param dateStr    时间字符串
         * @param timeFormat 时间格式转化器
         * @return java.sql.Date
         */
        public static java.sql.Date stringToSqlDate(String dateStr, TimeFormat timeFormat) {
            return Convert.localDateToSqlDate(stringToLocalDate(dateStr, timeFormat));
        }

        /**
         * 将时间字符串通过默认格式转换器，转换为时间
         *
         * @param dateStr 时间字符串
         * @return java.sql.Date
         */
        public static java.sql.Date stringToSqlDate(String dateStr) {
            return Convert.localDateToSqlDate(stringToLocalDate(dateStr));
        }

        /**
         * 将时间字符串通过格式转换器，转换为时间
         *
         * @param dateStr    时间字符串
         * @param timeFormat 时间格式转化器
         * @return java.sql.Timestamp
         */
        public static Timestamp stringToTimestamp(String dateStr, TimeFormat timeFormat) {
            return Convert.localDateTimeToTimestamp(stringToLocalDateTime(dateStr, timeFormat));
        }

        /**
         * 将时间字符串通过默认格式转换器，转换为时间
         *
         * @param dateStr 时间字符串
         * @return java.sql.Timestamp
         */
        public static Timestamp stringToTimestamp(String dateStr) {
            return Convert.localDateTimeToTimestamp(stringToLocalDateTime(dateStr));
        }

        /**
         * 将时间字符串通过格式转换器，转换为时间
         *
         * @param dateStr    时间字符串
         * @param timeFormat 时间格式转化器
         * @return java.sql.Time
         */
        public static java.sql.Time stringToSqlTime(String dateStr, TimeFormat timeFormat) {
            return Convert.localTimeToSqlTime(stringToLocalTime(dateStr, timeFormat));
        }

        /**
         * 将时间字符串通过默认格式转换器，转换为时间
         *
         * @param dateStr 时间字符串
         * @return java.sql.Time
         */
        public static java.sql.Time stringToSqlTime(String dateStr) {
            return Convert.localTimeToSqlTime(stringToLocalTime(dateStr));
        }

        /*
         *
         * java.sql.* / java.util.Date / java.time.* -> java.lang.String
         *
         * */
        //java.time.* -> java.lang.String

        /**
         * 将时间通过格式化转换器，转换为对应的字符串
         *
         * @param localDateTime 需要格式化的日期
         * @param timeFormat    时间格式转化器
         * @return java.lang.String
         */
        public static String localDateTimeToString(LocalDateTime localDateTime, TimeFormat timeFormat) {
            if (Objects.isNull(localDateTime) || Objects.isNull(timeFormat)) {
                return Const.EMPTY_STRING_OBJECT;
            }
            return timeFormat.formatter.format(localDateTime);
        }

        /**
         * 将时间通过默认格式化转换器，转换为对应的字符串
         *
         * @param localDateTime 需要格式化的日期
         * @return java.lang.String
         */
        public static String localDateTimeToString(LocalDateTime localDateTime) {
            return localDateTimeToString(localDateTime, Const.DEFAULT_DATETIME_FORMATTER);
        }

        /**
         * 将时间通过格式化转换器，转换为对应的字符串
         *
         * @param localDate  需要格式化的日期
         * @param timeFormat 时间格式转化器
         * @return java.lang.String
         */
        public static String localDateToString(LocalDate localDate, TimeFormat timeFormat) {
            if (Objects.isNull(localDate) || Objects.isNull(timeFormat)) {
                return Const.EMPTY_STRING_OBJECT;
            }
            return timeFormat.formatter.format(localDate);
        }

        /**
         * 将时间通过默认格式化转换器，转换为对应的字符串
         *
         * @param localDate 需要格式化的日期
         * @return java.lang.String
         */
        public static String localDateToString(LocalDate localDate) {
            return localDateToString(localDate, Const.DEFAULT_DATE_FORMATTER);
        }

        /**
         * 将时间通过默认格式化转换器，转换为对应的字符串
         *
         * @param localTime  需要格式化的时间
         * @param timeFormat 时间格式转化器
         * @return java.lang.String
         */
        public static String localTimeToString(LocalTime localTime, TimeFormat timeFormat) {
            if (Objects.isNull(localTime) || Objects.isNull(timeFormat)) {
                return Const.EMPTY_STRING_OBJECT;
            }
            return timeFormat.formatter.format(localTime);
        }

        /**
         * 将时间通过默认格式化转换器，转换为对应的字符串
         *
         * @param localTime 需要格式化的时间
         * @return java.lang.String
         */
        public static String localTimeToString(LocalTime localTime) {
            return localTimeToString(localTime, Const.DEFAULT_TIME_FORMATTER);
        }

        //java.util.Date -> String

        /**
         * 将时间通过格式化转换器，转换为对应的字符串
         *
         * @param date       需要格式化的时间
         * @param timeFormat 时间格式转化器
         * @return java.lang.String
         */
        public static String dateToString(java.util.Date date, TimeFormat timeFormat) {
            return localDateTimeToString(Convert.dateToLocalDateTime(date), timeFormat);
        }

        /**
         * 将时间通过默认默认格式化转换器，转换为对应的字符串
         *
         * @param date 需要格式化的时间
         * @return java.lang.String
         */
        public static String dateToString(java.util.Date date) {
            return dateToString(date, Const.DEFAULT_DATETIME_FORMATTER);
        }

        //java.sql.* -> String

        /**
         * 将时间通过格式化转换器，转换为对应的字符串
         *
         * @param timestamp  需要格式化的时间
         * @param timeFormat 时间格式转化器
         * @return java.lang.String
         */
        public static String timestampToString(Timestamp timestamp, TimeFormat timeFormat) {
            return localDateTimeToString(Convert.timestampToLocalDateTime(timestamp), timeFormat);
        }

        /**
         * 将时间通过默认格式化转换器，转换为对应的字符串
         *
         * @param timestamp 需要格式化的时间
         * @return java.lang.String
         */
        public static String timestampToString(Timestamp timestamp) {
            return timestampToString(timestamp, Const.DEFAULT_DATETIME_FORMATTER);
        }

        /**
         * 将时间通过格式化转换器，转换为对应的字符串
         *
         * @param time       需要格式化的时刻
         * @param timeFormat 时间格式转化器
         * @return java.lang.String
         */
        public static String sqlTimeToString(java.sql.Time time, TimeFormat timeFormat) {
            return localTimeToString(Convert.sqlTimeToLocalTime(time), timeFormat);
        }

        /**
         * 将时间通过默认格式化转换器，转换为对应的字符串
         *
         * @param time 需要格式化的时刻
         * @return java.lang.String
         */
        public static String sqlTimeToString(java.sql.Time time) {
            return sqlTimeToString(time, Const.DEFAULT_TIME_FORMATTER);
        }

        /**
         * 将时间通过格式化转换器，转换为对应的字符串
         *
         * @param date       需要格式化的日期
         * @param timeFormat 时间格式转化器
         * @return java.lang.String
         */
        public static String sqlDateToString(java.sql.Date date, TimeFormat timeFormat) {
            return localDateToString(Convert.sqlDateToLocalDate(date), timeFormat);
        }

        /**
         * 将时间通过默认格式化转换器，转换为对应的字符串
         *
         * @param date 需要格式化的日期
         * @return java.lang.String
         */
        public static String sqlDateToString(java.sql.Date date) {
            return sqlDateToString(date, Const.DEFAULT_DATE_FORMATTER);
        }
    }

    /**
     * <h2>时间计算（加减）</h2>
     * <p> 时间计算中主要从从两个维度展开计算：</p>
     * <ul>
     * <li>一个是以时间（日期）类的属性进行计算，有的可以精确到秒，有的只能精确到天（日期类）;</li>
     * <li>另一个则是以周为单位计算。</li>
     * </ul>
     * <p>
     * 计算方式主要为加减法计算，本工具类不从本质上区分加减法，他们的一致通过传入参数的正负来完成。
     * </p>
     */
    public static class Calculate {

        /**
         * 私有构造，工具类不允许实例化
         */
        private Calculate() {
        }

        /**
         * 时间计算，对提供的时间以最小精确到秒为单位计算
         *
         * @param localDateTime 时间
         * @param year          年
         * @param month         月
         * @param day           天（日）
         * @param hour          小时
         * @param minute        分钟
         * @param second        秒
         * @return java.time.LocalDateTime
         */
        public static LocalDateTime localDateTimePlusCal(LocalDateTime localDateTime, int year, int month, int day, int hour, int minute, int second) {
            if (Objects.isNull(localDateTime)) {
                return Const.NULL_LOCALDATETIME_OBJECT;
            }
            return localDateTime.plusYears(year)
                    .plusMonths(month)
                    .plusDays(day)
                    .plusHours(hour)
                    .plusMinutes(minute)
                    .plusSeconds(second);
        }

        /**
         * 时间计算，对提供的时间以周为单位进行计算
         *
         * @param localDateTime 时间
         * @param week          周数
         * @return java.time.LocalDateTime
         */
        public static LocalDateTime localDateTimePlusWeekCal(LocalDateTime localDateTime, int week) {
            if (Objects.isNull(localDateTime)) {
                return Const.NULL_LOCALDATETIME_OBJECT;
            }
            return localDateTime.plusWeeks(week);
        }

        /**
         * 日期计算， 对提供的日期以最小精确到天进行计算
         *
         * @param localDate 时间
         * @param year      年
         * @param month     月
         * @param day       天
         * @return java.time.LocalDate
         */
        public static LocalDate localDatePlusCal(LocalDate localDate, int year, int month, int day) {
            if (Objects.isNull(localDate)) {
                return Const.NULL_LOCALDATE_OBJECT;
            }
            return localDate.plusYears(year)
                    .plusMonths(month)
                    .plusDays(day);
        }

        /**
         * 时间计算，对提供的时间以周为单位进行计算
         *
         * @param localDate 时间
         * @param week      周
         * @return java.time.LocalDate
         */
        public static LocalDate localDatePlusWeekCal(LocalDate localDate, int week) {
            if (Objects.isNull(localDate)) {
                return Const.NULL_LOCALDATE_OBJECT;
            }
            return localDate.plusWeeks(week);
        }

        /**
         * 时间计算，正对时刻以最小单位为秒为单位计算
         *
         * @param localTime 时间
         * @param hour      小时
         * @param minute    分钟
         * @param second    秒
         * @return java.time.LocalTime
         */
        public static LocalTime localTimePlusCal(LocalTime localTime, int hour, int minute, int second) {
            if (Objects.isNull(localTime)) {
                return Const.NULL_LOCALTIME_OBJECT;
            }
            return localTime.plusHours(hour)
                    .plusMinutes(minute)
                    .plusSeconds(second);
        }

        /**
         * 计算 java.util.Date 时间，以最小精确到秒为单位计算
         *
         * @param date   时间
         * @param year   年
         * @param month  月
         * @param day    天
         * @param hour   小时
         * @param minute 分钟
         * @param second 秒
         * @return java.util.Date
         */
        public static java.util.Date datePlusCal(java.util.Date date, int year, int month, int day, int hour, int minute, int second) {
            return Convert.localDateTimeToDate
                    (localDateTimePlusCal
                            (Convert.dateToLocalDateTime(date), year, month, day, hour, minute, second));
        }

        /**
         * 计算 java.util.Date ,对提供的时间以周为单位计算。
         *
         * @param date 时间
         * @param week 周数
         * @return java.util.Date
         */
        public static java.util.Date datePlusWeekCal(java.util.Date date, int week) {
            return Convert.localDateTimeToDate
                    (localDateTimePlusWeekCal
                            (Convert.dateToLocalDateTime(date), week));
        }

        /**
         * 计算 java.sql.Timestamp 时间， 以最小精确到秒为单位进行计算
         *
         * @param timestamp 时间
         * @param year      年
         * @param month     月
         * @param day       天
         * @param hour      小时
         * @param minute    分
         * @param second    秒
         * @return java.sql.Timestamp
         */
        public static Timestamp timestampPlusCal(Timestamp timestamp, int year, int month, int day, int hour, int minute, int second) {
            return Convert.localDateTimeToTimestamp
                    (localDateTimePlusCal
                            (Convert.timestampToLocalDateTime(timestamp), year, month, day, hour, minute, second));
        }

        /**
         * 计算 java.sql.Timestamp 时间，对提供的时间以周为单位进行计算
         *
         * @param timestamp 时间
         * @param week      周数
         * @return java.sql.Timestamp
         */
        public static Timestamp timestampPlusWeekCal(Timestamp timestamp, int week) {
            return Convert.localDateTimeToTimestamp
                    (localDateTimePlusWeekCal
                            (Convert.timestampToLocalDateTime(timestamp), week));
        }

        /**
         * 计算 java.sql.Date 日期，对提供的日期以最小精确到天为单位计算
         *
         * @param date  日期
         * @param year  年
         * @param month 月
         * @param day   天
         * @return java.sql.Date
         */
        public static java.sql.Date sqlDatePlusCal(java.sql.Date date, int year, int month, int day) {
            return Convert.localDateToSqlDate
                    (localDatePlusCal
                            (Convert.sqlDateToLocalDate(date), year, month, day));
        }

        /**
         * java.sql.Date 计算，对提供的时间以周为单位进行加减计算
         *
         * @param date 日期
         * @param week 周数
         * @return java.sql.Date
         */
        public static java.sql.Date sqlDatePlusWeekCal(java.sql.Date date, int week) {
            return Convert.localDateToSqlDate
                    (localDatePlusWeekCal
                            (Convert.sqlDateToLocalDate(date), week));
        }

        /**
         * java.sql.Time 计算，加（减）。
         * <p>
         * 计算规则：
         * </p>
         * 提供需要进行计算的时间，以及需要对应的时，分，秒等。
         * 加法时，提供正值；减法计算时，提供负数。
         *
         * @param time   时间
         * @param hour   小时
         * @param minute 分（钟）
         * @param second 秒
         * @return java.sql.Time
         */
        public static java.sql.Time sqlTimePlusCal(java.sql.Time time, int hour, int minute, int second) {
            return Convert.localTimeToSqlTime
                    (localTimePlusCal
                            (Convert.sqlTimeToLocalTime(time), hour, minute, second));
        }
    }

    /**
     * <h2>时间判断</h2>
     * <p>时间判断中提供的方法通常都包括两个参数内容（有的会有时间格式转化器），其中计算规则如下：</p>
     * <ul>
     * <li>
     * 若无需要时间转换器的计算方法，通常结果计算规则：
     * 第二个参数的时间（second） - 第一个参数的时间（first）
     * </li>
     * <li>
     * 若需要时间格式转换器的计算方法，通常计算规则：
     * 第二个时间字符串对应时间（通过转换器转换时间类） - 第一个时间字符串对应的时间（通过转换器转换为时间类）
     * </li>
     * </ul>
     *
     * @since 5.0 (jdk 1.8)
     */
    public static class Judge {

        /**
         * 私有构造，工具类不允许实例化
         */
        private Judge() {
        }

        /**
         * <p>若第二个时间小于第一个时间，则返回-1.(first > second -> -1);
         * 两个输入时间相等则返回零(first == second -> 0)；
         * 如果第二个时间大于第一个时间，则返回1（first < second -> 1）。
         * </p>
         *
         * @param first  第一个时间
         * @param second 第二个时间
         * @return int
         * @since 5.0 (jdk 1.8)
         */
        public static int compare(LocalDateTime first, LocalDateTime second) {
            return second.compareTo(first);
        }

        /**
         * 计算两个时间之前的间隔，返回对象为5.0 (jdk 1.8)提供的 java.time.Duration 类。
         *
         * @param first  第一个时间
         * @param second 第二个时间
         * @return java.time.Duration
         * @see Duration
         */
        public static Duration intervalResultInDuration(LocalDateTime first, LocalDateTime second) {
            if (Objects.isNull(first) || Objects.isNull(second)) {
                throw new IllegalArgumentException("first or second cannot be null");
            }
            return Duration.between(first, second);
        }

        /**
         * 计算两个时间之间的间隔分钟
         *
         * @param first  第一个时间
         * @param second 第二个时间
         * @return java.lang.Long
         */
        public static Long intervalResultInMinute(LocalDateTime first, LocalDateTime second) {
            Duration duration = intervalResultInDuration(first, second);
            if (Objects.isNull(duration)) {
                throw new IllegalArgumentException("duration cannot be null");
            }
            return duration.toMinutes();
        }


        /**
         * 计算两个时间之间间隔的毫秒
         *
         * @param first  第一个时间
         * @param second 第二个时间
         * @return java.lang.Long
         */
        public static Long intervalResultInMill(LocalDateTime first, LocalDateTime second) {
            Duration duration = intervalResultInDuration(first, second);
            if (Objects.isNull(duration)) {
                throw new IllegalArgumentException("duration cannot be null");
            }
            return duration.toMillis();
        }

        /**
         * 计算两个时间之间间隔的小时
         *
         * @param first  第一个时间
         * @param second 第二个时间
         * @return java.lang.Long
         */
        public static Long intervalResultInHour(LocalDateTime first, LocalDateTime second) {
            Duration duration = intervalResultInDuration(first, second);
            if (Objects.isNull(duration)) {
                throw new IllegalArgumentException("duration cannot be null");
            }
            return duration.toHours();
        }

        /**
         * 计算两个时间刻度之间的间隔，返回 java.time.Duration
         *
         * @param first  第一个时间
         * @param second 第二个时间
         * @return java.time.Duration
         * @see Duration
         */
        public static Duration intervalResultInDuration(LocalTime first, LocalTime second) {
            if (Objects.isNull(first) || Objects.isNull(second)) {
                return null;
            }
            return Duration.between(first, second);
        }

        /**
         * 计算两个时间之间间隔小时
         *
         * @param first  第一个时间
         * @param second 第二个时间
         * @return java.lang.Long
         */
        public static Long intervalResultInHour(LocalTime first, LocalTime second) {
            Duration duration = intervalResultInDuration(first, second);
            if (Objects.isNull(duration)) {
                throw new IllegalArgumentException("duration cannot be null");
            }
            return duration.toHours();
        }

        /**
         * 计算两个日期之间间隔，返回结果为 java.time.Period
         *
         * @param first  第一个时间
         * @param second 第二个时间
         * @return java.time.Period
         * @see Period
         */
        public static Period intervalResultInPeriod(LocalDate first, LocalDate second) {
            if (Objects.isNull(first) || Objects.isNull(second)) {
                throw new IllegalArgumentException("first or second cannot be null");
            }
            return Period.between(first, second);
        }

        /**
         * 计算两个时间之间间隔多少年
         *
         * @param first  第一个时间
         * @param second 第二个时间
         * @return int
         */
        public static int intervalResultInYear(LocalDate first, LocalDate second) {
            Period period = intervalResultInPeriod(first, second);
            if (Objects.isNull(period)) {
                throw new IllegalArgumentException("period cannot be null");
            }
            return period.getYears();
        }

        /**
         * 计算两个时间之间间隔多少个月
         *
         * @param first  第一个时间
         * @param second 第二个时间
         * @return int
         */
        public static int intervalResultInMonth(LocalDate first, LocalDate second) {
            Period period = intervalResultInPeriod(first, second);
            if (Objects.isNull(period)) {
                throw new IllegalArgumentException("first or second cannot be null");
            }
            return period.getYears() * 12 + period.getMonths();
        }

        /**
         * 计算两个时间间隔多少天
         *
         * @param first  第一个时间
         * @param second 第二个时间
         * @return java.lang.Long
         */
        public static Long intervalResultInDay(LocalDate first, LocalDate second) {
            if (Objects.isNull(first) || Objects.isNull(second)) {
                throw new IllegalArgumentException("first or second cannot be null");
            }
            return second.toEpochDay() - first.toEpochDay();
        }

        /**
         * 计算两个时间之间间隔多少天
         *
         * @param first  第一个时间
         * @param second 第二个时间
         * @return java.lang.Long
         */
        public static Long intervalResultInDay(LocalDateTime first, LocalDateTime second) {
            return intervalResultInDay(first.toLocalDate(), second.toLocalDate());
        }

        /**
         * 计算两个时间之间间隔多少年
         *
         * @param first  第一个时间
         * @param second 第二个时间
         * @return int
         */
        public static int intervalResultInYear(java.util.Date first, java.util.Date second) {
            return intervalResultInYear(Convert.dateToLocalDate(first), Convert.dateToLocalDate(second));
        }

        /**
         * 计算两个时间之间间隔多少年
         *
         * @param firstDateStr  第一个时间字符串
         * @param secondDateStr 第二个时间字符串
         * @param timeFormat    时间格式转化器
         * @return int
         */
        public static int intervalResultInYear(String firstDateStr, String secondDateStr, TimeFormat timeFormat) {
            return intervalResultInYear(Format.stringToLocalDate(firstDateStr, timeFormat), Format.stringToLocalDate(secondDateStr, timeFormat));
        }

        /**
         * 计算两个时间之间间隔多少年
         *
         * @param first  第一个时间
         * @param second 第二个时间
         * @return int
         */
        public static int intervalResultInYear(Timestamp first, Timestamp second) {
            return intervalResultInYear(Convert.timestampToLocalDate(first), Convert.timestampToLocalDate(second));
        }

        /**
         * 计算两个日期之间间隔多少年
         *
         * @param first  第一个时间
         * @param second 第二个时间
         * @return int
         */
        public static int intervalResultInYear(java.sql.Date first, java.sql.Date second) {
            return intervalResultInYear(Convert.sqlDateToLocalDate(first), Convert.sqlDateToLocalDate(second));
        }

        /**
         * 计算两个时间之间间隔多少个月
         *
         * @param first  第一个时间
         * @param second 第二个时间
         * @return int
         */
        public static int intervalResultInMonth(java.util.Date first, java.util.Date second) {
            return intervalResultInMonth(Convert.dateToLocalDate(first), Convert.dateToLocalDate(second));
        }

        /**
         * 计算两个时间之间间隔多少月
         *
         * @param first  第一个时间
         * @param second 第二个时间
         * @return int
         */
        public static int intervalResultInMonth(Timestamp first, Timestamp second) {
            return intervalResultInMonth(Convert.timestampToLocalDate(first), Convert.timestampToLocalDate(second));
        }

        /**
         * 计算两个日期之间间隔多少月
         *
         * @param first  第一个时间
         * @param second 第二个时间
         * @return int
         */
        public static int intervalResultInMonth(java.sql.Date first, java.sql.Date second) {
            return intervalResultInMonth(Convert.sqlDateToLocalDate(first), Convert.sqlDateToLocalDate(second));
        }

        /**
         * 计算两个日期之间间隔多少天
         *
         * @param first  第一个时间
         * @param second 第二个时间
         * @return java.lang.Long
         */
        public static Long intervalResultInDay(java.util.Date first, java.util.Date second) {
            return intervalResultInDay(Convert.dateToLocalDate(first), Convert.dateToLocalDate(second));
        }

        /**
         * 计算两个时间之间间隔多少天
         *
         * @param first  第一个时间
         * @param second 第二个时间
         * @return java.lang.Long
         */
        public static Long intervalResultInDay(java.sql.Date first, java.sql.Date second) {
            return intervalResultInDay(Convert.sqlDateToLocalDate(first), Convert.sqlDateToLocalDate(second));
        }

        /**
         * 计算两个时间之间间隔多少天
         *
         * @param first  第一个时间
         * @param second 第二个时间
         * @return java.lang.Long
         */
        public static Long intervalResultInDay(Timestamp first, Timestamp second) {
            return intervalResultInDay(Convert.timestampToLocalDate(first), Convert.timestampToLocalDate(second));
        }

        /**
         * 计算两个时间间隔多少小时
         *
         * @param first  第一个时间
         * @param second 第二个时间
         * @return java.lang.Long
         */
        public static Long intervalResultInHour(java.sql.Time first, java.sql.Time second) {
            return intervalResultInHour(Convert.sqlTimeToLocalTime(first), Convert.sqlTimeToLocalTime(second));
        }

        /**
         * 计算两个时间的间隔有多少个小时
         *
         * @param firstTimeStr  第一个时间字符串
         * @param secondTimeStr 第二个时间字符串
         * @param timeFormat    时间格式转化器
         * @return java.lang.Long
         */
        public static Long intervalResultInHour(String firstTimeStr, String secondTimeStr, TimeFormat timeFormat) {
            return intervalResultInHour(Format.stringToLocalTime(firstTimeStr, timeFormat), Format.stringToLocalTime(secondTimeStr, timeFormat));
        }

        /**
         * 判断两个时间的大小，结果以小时返回，正值代表第二个比第一个大多少个小时，反之亦然
         *
         * @param first  第一个时间
         * @param second 第二个时间
         * @return java.lang.Long
         *
         */
        public static Long intervalResultInHour(Timestamp first, Timestamp second) {
            return intervalResultInHour(Convert.timestampToLocalDateTime(first), Convert.timestampToLocalDateTime(second));
        }

        /**
         * 判断两个时间的大小，结果以小时返回，正值代表第二个比第一个大多少个小时，反之亦然
         *
         * @param first  第一个时间
         * @param second 第二个时间
         * @return java.lang.Long
         *
         */
        public static Long intervalResultInHour(java.util.Date first, java.util.Date second) {
            return intervalResultInHour(Convert.dateToLocalDateTime(first), Convert.dateToLocalDateTime(second));
        }

        /**
         * 判断两个时间的大小，结果以分钟返回，正值代表第二个比第一个大多少个小时，反之亦然
         *
         * @param first  第一个时间
         * @param second 第二个时间
         * @return java.lang.Long
         *
         */
        public static Long intervalResultInMinute(Timestamp first, Timestamp second) {
            return intervalResultInMinute(Convert.timestampToLocalDateTime(first), Convert.timestampToLocalDateTime(second));
        }

        /**
         * 判断两个时间的大小，结果以分钟返回，正值代表第二个比第一个大多少个小时，反之亦然
         *
         * @param first  第一个时间
         * @param second 第二个时间
         * @return java.lang.Long
         *
         */
        public static Long intervalResultInMinute(java.util.Date first, java.util.Date second) {
            return intervalResultInMinute(Convert.dateToLocalDateTime(first), Convert.dateToLocalDateTime(second));
        }

        /**
         * 判断两个时间的大小，结果以毫秒返回，正值代表第二个比第一个大多少个小时，反之亦然
         *
         * @param first  第一个时间
         * @param second 第二个时间
         * @return java.lang.Long
         *
         */
        public static Long intervalResultInMill(Timestamp first, Timestamp second) {
            return intervalResultInMill(Convert.timestampToLocalDateTime(first), Convert.timestampToLocalDateTime(second));
        }

        /**
         * 判断两个时间的大小，结果以毫秒返回，正值代表第二个比第一个大多少个小时，反之亦然
         *
         * @param first  第一个时间
         * @param second 第二个时间
         * @return java.lang.Long
         *
         */
        public static Long intervalResultInMill(java.util.Date first, java.util.Date second) {
            return intervalResultInMill(Convert.dateToLocalDateTime(first), Convert.dateToLocalDateTime(second));
        }
    }

    /**
     * <h2>时区转换相关计算</h2>
     * <p>
     * 时区相关计算以及转换工作，本工具中提供了5.0 (jdk 1.8)的时间核心包java.time.* 中的LocalDateTime/LocalDate/LocalTime 等相关时区工具。
     * </p>
     * <p>
     * 5.0 (jdk 1.8) 版本之前的时间类(java.util.Date/java.sql.*)可通过DateUtil.Convert.*下的工具转换为开发需要的时间日期类。
     * </p>
     * <p>
     * 时区工具其中的主要工具包括2个方面：
     * <ul>
     * <li>
     * a.根据时区获取当前时间。
     * </li>
     * <li>
     * b. 根据提供的时间、时间所在时区（有的为系统默认时区）以及目标时区，计算目标时区的对应时间。
     * </li>
     * </ul>
     * </p>
     * <p>
     * 提示：
     * <ul>
     * <li>
     * 如果需要计算两个时区之间的时差，可以取其中一个时区的时间点作为参考点，计算目标时区对应时间。
     * </li>
     * <li>
     * 然后通过DateUtil.Judge.*下的方法计算参考时间于目标时间之间的间隔时间。
     * </li>
     * </ul>
     * </p>
     */
    public static class ZoneConvert {

        /**
         * 私有构造，工具类不可实例化
         */
        private ZoneConvert() {
        }

        /**
         * 根据提供的时区ID，获取该时区的当前时间
         * eg:
         * <p>
         * DateUtils.ZoneConvert.getLocalDateTimeByZone(DateUtils.Zone.ECT.zoneId)
         * </p>
         *
         * @param zoneId 时区ID
         * @return java.time.LocalDateTime
         */
        public static LocalDateTime getLocalDateTimeByZone(ZoneId zoneId) {
            if (Objects.isNull(zoneId)) {
                return Const.NULL_LOCALDATETIME_OBJECT;
            }
            return LocalDateTime.now(zoneId);
        }

        /**
         * 根据提供的时区ID,获取该时区当前的日期。
         * eg:
         * <p>
         * DateUtils.ZoneConvert.getLocalDateByZone(DateUtils.Zone.ECT.zoneId)
         * </p>
         *
         * @param zoneId 时区ID
         * @return java.time.LocalDate
         */
        public static LocalDate getLocalDateByZone(ZoneId zoneId) {
            if (Objects.isNull(zoneId)) {
                return Const.NULL_LOCALDATE_OBJECT;
            }
            return LocalDate.now(zoneId);
        }

        /**
         * 根据提供的时区ID,获取该时区当前时间点
         * eg:
         * <p>
         * DateUtils.ZoneConvert.getLocalTimeByZone(DateUtils.Zone.ECT.zoneId)
         * </p>
         *
         * @param zoneId 时区ID
         * @return java.time.LocalTime
         */
        public static LocalTime getLocalTimeByZone(ZoneId zoneId) {
            if (Objects.isNull(zoneId)) {
                return Const.NULL_LOCALTIME_OBJECT;
            }
            return LocalTime.now(zoneId);
        }

        /**
         * 根据提供的时间，以及该时间所在时区，计算目标时区的对应时间。
         *
         * @param localDateTime 原时间
         * @param srcZoneId     原时间所在时区ID
         * @param destZoneId    需要计算的目标时区ID
         * @return java.time.LocalDateTime
         */
        public static LocalDateTime getLocalDateTimeByZone(LocalDateTime localDateTime, ZoneId srcZoneId, ZoneId destZoneId) {
            if (Objects.isNull(localDateTime) || Objects.isNull(srcZoneId) || Objects.isNull(destZoneId)) {
                return Const.NULL_LOCALDATETIME_OBJECT;
            }
            ZoneOffset zoneOffset = localDateTime.atZone(destZoneId).getOffset();
            return localDateTime.atZone(srcZoneId).toInstant().atOffset(zoneOffset).toLocalDateTime();
        }

        /**
         * 根据提供的日期以及该日期所在的时区，计算目标时区对应的日期。
         *
         * @param localDate  原日期
         * @param srcZoneId  原时间所在时区ID
         * @param destZoneId 需要计算的目标时区ID
         * @return java.time.LocalDate
         */
        public static LocalDate getLocalDateByZone(LocalDate localDate, ZoneId srcZoneId, ZoneId destZoneId) {
            return getLocalDateTimeByZone(localDate.atStartOfDay(), srcZoneId, destZoneId).toLocalDate();
        }

        /**
         * 根据提供的时间以及系统默认时区，计算对应时区的对应时间。
         *
         * @param localDateTime 原时间
         * @param zoneId        时区ID
         * @return java.time.LocalDateTime
         */
        public static LocalDateTime getLocalDateTimeByZone(LocalDateTime localDateTime, ZoneId zoneId) {
            return getLocalDateTimeByZone(localDateTime, ZoneId.systemDefault(), zoneId);
        }

        /**
         * 根据提供的日期，以及系统默认时区，计算目标时区对应的时间。
         *
         * @param localDate 原日期
         * @param zoneId    时区ID
         * @return java.time.LocalDate
         */
        public static LocalDate getLocalDateByZone(LocalDate localDate, ZoneId zoneId) {
            return getLocalDateTimeByZone(localDate.atStartOfDay(), zoneId).toLocalDate();
        }

        /**
         * 根据提供的时区ID，获取该时区当前时间。
         *
         * @param zoneId 时区ID
         * @return java.util.Date
         */
        public static java.util.Date getDateByZone(ZoneId zoneId) {
            return Convert.localDateTimeToDate(getLocalDateTimeByZone(zoneId));
        }

        /**
         * 根据提供的时间以及系统默认时区，计算对应时区的对应时间。
         *
         * @param date   时间
         * @param zoneId 时区ID
         * @return java.util.Date
         */
        public static java.util.Date getDateByZone(java.util.Date date, ZoneId zoneId) {
            return Convert.localDateTimeToDate
                    (getLocalDateTimeByZone
                            (Convert.dateToLocalDateTime(date), ZoneId.systemDefault(), zoneId));
        }

        /**
         * 根据提供的日期以及该日期所在的时区，计算目标时区对应的日期。
         *
         * @param date       时间
         * @param srcZoneId  原时间所在时区ID
         * @param destZoneId 需要计算的目标时区ID
         * @return java.util.Date
         */
        public static java.util.Date getDateByZone(java.util.Date date, ZoneId srcZoneId, ZoneId destZoneId) {
            return Convert.localDateTimeToDate
                    (getLocalDateTimeByZone
                            (Convert.dateToLocalDateTime(date), srcZoneId, destZoneId));
        }


    }

    /**
     * 提供默认的内置格式转换器以及可自定义的格式转换器。
     *
     * @since 5.0 (jdk 1.8)
     */
    public static class TimeFormat {
        /**
         * 短时间格式: HH:mm:ss
         */
        public static final TimeFormat HH_MM_SS_COLON = new TimeFormat("HH:mm:ss");
        /**
         * 短时间格式: HH:mm:ss.SSS
         */
        public static final TimeFormat HH_MM_SS_MILSEC_COLON = new TimeFormat("HH:mm:ss.SSS");
        /**
         * 短时间格式: HH时mm分ss秒"
         */
        public static final TimeFormat HH_MM_SS_CHINESE = new TimeFormat("HH时mm分ss秒");

        /**
         * 短时间格式: yyyy年MM月dd日
         */
        public static final TimeFormat YYYY_MM_DD_CHINESE = new TimeFormat("yyyy年MM月dd日");
        /**
         * 短时间格式: yyyy-MM-dd
         */
        public static final TimeFormat YYYY_MM_DD_LINE = new TimeFormat("yyyy-MM-dd");
        /**
         * 短时间格式: yyyy/MM/dd
         */
        public static final TimeFormat YYYY_MM_DD_SLASH = new TimeFormat("yyyy/MM/dd");
        /**
         * 短时间格式: yyyy\MM\dd
         */
        public static final TimeFormat YYYY_MM_DD_BACKSLASH = new TimeFormat("yyyy\\MM\\dd");
        /**
         * 短时间格式: yyyyMMdd
         */
        public static final TimeFormat YYYY_MM_DD_NONE = new TimeFormat("yyyyMMdd");

        /**
         * 期号格式：yyyyMM
         */
        public static final TimeFormat YYYY_MM = new TimeFormat("yyyyMM");

        /**
         * 长时间格式： "yyyy年MM月dd日 HH时mm分ss秒"
         */
        public static final TimeFormat YYYY_MM_DD_HH_MM_SS_CHINESE = new TimeFormat("yyyy年MM月dd日 HH时mm分ss秒");
        /**
         * 长时间格式: yyyy-MM-dd HH:mm:ss
         */
        public static final TimeFormat YYYY_MM_DD_HH_MM_SS_LINE = new TimeFormat("yyyy-MM-dd HH:mm:ss");
        /**
         * 长时间格式: yyyy/MM/dd HH:mm:ss
         */
        public static final TimeFormat YYYY_MM_DD_HH_MM_SS_SLASH = new TimeFormat("yyyy/MM/dd HH:mm:ss");
        /**
         * 长时间格式: yyyy\MM\dd HH:mm:ss
         */
        public static final TimeFormat YYYY_MM_DD_HH_MM_SS_BACKSLASH = new TimeFormat("yyyy\\MM\\dd HH:mm:ss");
        /**
         * 长时间格式: yyyyMMdd HH:mm:ss
         */
        public static final TimeFormat YYYY_MM_DD_HH_MM_SS_NONE = new TimeFormat("yyyyMMdd HH:mm:ss");
        /**
         * 长时间格式：yyyyMMddHHmmss
         */
        public static final TimeFormat YYYY_MM_DD_HH_MM_SS_BUS = new TimeFormat("yyyyMMddHHmmss");
        /**
         * 长时间格式 带毫秒： yyyy-MM-dd HH:mm:ss.SSS
         */
        public static final TimeFormat YYYY_MM_DD_HH_MM_SS_MILSEC_LINE = new TimeFormat("yyyy-MM-dd HH:mm:ss.SSS");
        /**
         * 长时间格式 带毫秒： yyyy/MM/dd HH:mm:ss.SSS
         */
        public static final TimeFormat YYYY_MM_DD_HH_MM_SS_MILSEC_SLASH = new TimeFormat("yyyy/MM/dd HH:mm:ss.SSS");
        /**
         * 长时间格式 带毫秒： yyyy\MM\dd HH:mm:ss.SSS
         */
        public static final TimeFormat YYYY_MM_DD_HH_MM_SS_MILSEC_BACKSLASH = new TimeFormat("yyyy\\MM\\dd HH:mm:ss.SSS");
        /**
         * 长时间格式 带毫秒： yyyyMMdd HH:mm:ss.SSS
         */
        public static final TimeFormat YYYY_MM_DD_HH_MM_SS_MILSEC_NONE = new TimeFormat("yyyyMMdd HH:mm:ss.SSS");

        private DateTimeFormatter formatter;
        private String pattern;

        private TimeFormat(String pattern) {
            this.pattern = pattern;
            formatter = DateTimeFormatter.ofPattern(pattern);
        }

        /**
         * 如果传入字符串不为空（null != pattern && !"".equals(pattern)）,则返回一个新的时间格式转换器。否则返回NULL
         *
         * @param pattern 模式匹配
         * @return com.yinhai.ta404.core.utils.DateUtils.TimeFormat
         */
        public static TimeFormat getCustomPattern(String pattern) {
            if (InnerTools.isEmpty(pattern)) {
                return null;
            }
            return new TimeFormat(pattern);
        }

        public String getPattern() {
            return this.pattern;
        }
    }

    /**
     * 由于其它工具类的重载问题，自定义内部验证工具私有类。
     */
    private static class InnerTools {
        private InnerTools() {
        }

        /**
         * StringUtils 工具类中isEmpty无法使用，不依赖该工具类，自己提供字符串判空方法
         *
         * @param chs 需要判判断的字符串
         * @return boolean
         */
        private static boolean isEmpty(CharSequence chs) {
            return Objects.isNull(chs) || chs.length() == 0;
        }
    }
}
